/****************************************
File: framework.cpp

This file is part of Model-loader

Copyright (C) 1999 David Stibbe (dstibbe@gmail.com)
                     and Gerrit Jan Jansen

Stellar is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
****************************************/

#include "framework.h"


Fl_Button *playButton = NULL;
Fl_Button *stopButton = NULL;
Fl_Button *zoomInButton = NULL;
Fl_Button *zoomOutButton = NULL;
Fl_Button *movLButton = NULL;
Fl_Button *movRButton = NULL;
Fl_Button *movUButton = NULL;
Fl_Button *movDButton = NULL;
Fl_Button *frontButton = NULL;
Fl_Button *backButton = NULL;

GLWindow *openglWindow = NULL;
Fl_Value_Slider *rotincSlider = NULL;
Fl_Menu_Button *opdrachtMenu;
float rotationIncrement = 10.0;

int running = 0;    // true als de 'animatie' speelt

int numberOfVertices;	// aantal vertices in het model
vertex *vertices;			// alle vertices van het model
int numberOfFaces;		// aantal faces in het model
face *faces;				// alle faces van het model



int readModel(const char *filename) {
  int i;

  FILE *F = fopen(filename, "r");
  if (F == NULL) {
    fprintf(stderr, "readModel(): could not open %s\n", filename);
    return 0;
  }

  if (fscanf(F, "%d\n", &numberOfVertices) != 1) {
    fprintf(stderr, "readModel(): could not read number of vertices\n");
    fclose(F);
    return 0;
  }

  if ( (vertices = new vertex[numberOfVertices]) == NULL) {
    fprintf(stderr, "readModel(): could not allocate memory for vertices\n");
    fclose(F);
    return 0;
  }

  for (i = 0; i < numberOfVertices; i++)
  {
    if (fscanf(F, "%f %f %f\n", vertices[i].pos + 0, vertices[i].pos + 1, vertices[i].pos + 2) != 3)
    {
        fprintf(stderr, "readModel(): could not read vertex %d\n", i);
        free(vertices);
        fclose(F);
        return 0;
    }
    vertices[i].normal[0] = vertices[i].normal[1] = vertices[i].normal[2] = 0.0f;
  }

  if (fscanf(F, "%d\n", &numberOfFaces) != 1) {
    fprintf(stderr, "readModel(): could not read number of faces\n");
    free(vertices);
    fclose(F);
    return 0;
  }

  if ( (faces = (face*)malloc(sizeof(face) * numberOfFaces)) == NULL) {
    fprintf(stderr, "readModel(): could not allocate memory for faces\n");
    free(vertices);
    fclose(F);
    return 0;
  }

  for (i = 0; i < numberOfFaces; i++) {
    if (fscanf(F, "%d %d %d\n", faces[i].vertex + 0, faces[i].vertex + 1, faces[i].vertex + 2) == 0) {
      fprintf(stderr, "readModel(): could not read face %d\n", i);
      free(vertices);
      free(faces);
      fclose(F);
      return 0;
    }
    faces[i].normal[0] = faces[i].normal[1] = faces[i].normal[2] = 0.0f;
  }

  fclose(F);

  loadModel(numberOfVertices,vertices, numberOfFaces,faces );


  printf("%s: %d vertices, %d faces\n", filename, numberOfVertices, numberOfFaces);

  return 1;
}



void redraw() {
  openglWindow->redraw();
}

void timeoutFunc(void *) {
  if (running) { // Als er op de playButton is gedrukt,
    step(rotationIncrement);      // doe dan een 'stap' (->verplaats het figuur)
    redraw();    // en teken het figuur opnieuw.
  }

  // Vraag een nieuwe timeout aan voor over 1/25ste seconde
  Fl::add_timeout(1.0 / 25.0, timeoutFunc, NULL);
}

void buttonCallback(Fl_Widget *W, void *Data) {
	if (W == playButton) {
		running = !running;
		playButton->label((running) ? "@||" : "@>");
		playButton->redraw();
	}
	else if (W == stopButton) {
		reset();
		running = 0;
		playButton->label((running) ? "@||" : "@>");
		playButton->redraw();
		redraw();
	}
	else if (W == rotincSlider) {
		rotationIncrement = (float)rotincSlider->value();
	}
	else if (W == opdrachtMenu) {
		opdrachtMenu->label(opdrachtMenu->text());
		opdrachtMenu->redraw();
		redraw();
	}
	else if (W == zoomInButton) {
		zoom(10.0);
		redraw();
	}
	else if (W == zoomOutButton) {
		zoom(-10.0);
		redraw();
	}
	else if (W == movLButton) {
		moveX(rotationIncrement);
		redraw();
	}
	else if (W == movRButton) {
		moveX(-rotationIncrement);
		redraw();
	}
	else if (W == movUButton) {
		moveY(rotationIncrement);
		redraw();
	}
	else if (W == movDButton) {
		moveY(-rotationIncrement);
		redraw();
	}
	else if (W == frontButton) {
		viewFront();
		redraw();
	}
	else if (W == backButton) {
		viewBack();
		redraw();
	}

}

Fl_Menu_Item menuTable[] = {
	{"Wireframe", 0, buttonCallback, (void*)WIREFRAME, 0},
	{"Color", 0, buttonCallback, (void*)USE_COLOR, 0},
	{"Use Normal", 0, buttonCallback, (void*)USE_NORMAL, 0},
	{"Use Normal Smooth", 0, buttonCallback, (void*)USE_NORMAL_SMOOTH, 0},
	{0}
};

void createWindow(int argc, char *argv[]) {
/*
Maak het window aan:
*/
	// Maak een 'top-level' window.
	Fl_Window *window = new Fl_Window(600, 650, "Fltk-Model-Viewer");

	// Maak een childwindow (GLWindow)
	openglWindow = new GLWindow(5, 5, 590, 500, "GL Window");
	openglWindow->mode(FL_RGB | FL_DOUBLE | FL_DEPTH);			// stel opengl in op rgb kleur & double buffering

	Fl_Window *controlBarWindow = new Fl_Window(0, 510, 600, 140);
	controlBarWindow->box(FL_DOWN_BOX);

	// maak play button
	playButton = new Fl_Button(5,5,25,25, "@#>");		// @#> + FL_SYMBOL_LABEL zorgt voor het 'play teken'
	playButton->labeltype(FL_SYMBOL_LABEL);
	playButton->align(FL_ALIGN_CLIP);
	playButton->shortcut(FL_CTRL + 'p');				// CTRL-p is de shortcut voor de playbutton
	playButton->callback(buttonCallback, NULL);			// buttonCallback wordt aangeroepen als er op playbutton wordt gedrukt

	// maak stop button
	stopButton = new Fl_Button(35,5,25,25, "@#square");
	stopButton->labeltype(FL_SYMBOL_LABEL);
	stopButton->align(FL_ALIGN_CLIP);
	stopButton->shortcut(FL_CTRL + 's');
	stopButton->callback(buttonCallback, NULL);

	// maak 'rot inc' label
	Fl_Output *label = new Fl_Output(85, 5, 50, 25);
	label->box(FL_FLAT_BOX);
	label->color(FL_GRAY);
	label->insert("rot inc:");

	// make een slider om de rotation increment aan te geven
	rotincSlider = new Fl_Value_Slider (140, 5, 305, 25);
	rotincSlider->type(FL_HOR_SLIDER);
	rotincSlider->range(-45.0, 45.0);
	rotincSlider->step(0.1);
	rotincSlider->callback(buttonCallback, NULL);
	rotincSlider->value(rotationIncrement);

	opdrachtMenu = new Fl_Menu_Button(450, 5, 145, 25, "Wireframe");
	opdrachtMenu->menu(menuTable);
	opdrachtMenu->value(0);

	controlBarWindow->resizable(rotincSlider);

	zoomInButton = new Fl_Button(5,30,25,25, "+");
	zoomInButton->align(FL_ALIGN_CLIP);
	zoomInButton->shortcut('+');				// CTRL-p is de shortcut voor de playbutton
	zoomInButton->callback(buttonCallback, NULL);

	zoomOutButton  = new Fl_Button(5,55,25,25, "-");
	zoomOutButton->align(FL_ALIGN_CLIP);
	zoomOutButton->shortcut('-');
	zoomOutButton->callback(buttonCallback, NULL);

	movLButton = new Fl_Button(100,55,25,25, "<");
	movLButton->align(FL_ALIGN_CLIP);
	movLButton->shortcut('a');
	//movLButton->shortcut(FL_Left);
	movLButton->callback(buttonCallback, NULL);

	movRButton = new Fl_Button(150,55,25,25, ">");
	movRButton->align(FL_ALIGN_CLIP);
	movRButton->shortcut('d');
	//movRButton->shortcut(FL_Right);
	movRButton->callback(buttonCallback, NULL);

	movUButton = new Fl_Button(125,30,25,25, "^");
	movUButton->align(FL_ALIGN_CLIP);
	movUButton->shortcut('w');
	//movUButton->shortcut(FL_Up);
	movUButton->callback(buttonCallback, NULL);

	movDButton = new Fl_Button(125,80,25,25, "V");
	movDButton->align(FL_ALIGN_CLIP);
	movDButton->shortcut('s');
	//movDButton->shortcut(FL_Down);
	movDButton->callback(buttonCallback, NULL);

	frontButton = new Fl_Button(30,30,50,25, "front");
	frontButton->align(FL_ALIGN_CLIP);
	frontButton->type(FL_TOGGLE_BUTTON);
	frontButton->value(viewFront());
	frontButton->callback(buttonCallback, NULL);

	backButton = new Fl_Button(30,55,50,25, "back");
	backButton->align(FL_ALIGN_CLIP);
	backButton->type(FL_TOGGLE_BUTTON);
	backButton->callback(buttonCallback, NULL);


	controlBarWindow->end();

	// Vertel 'window' dat 'openglWindow' resizable is
	window->resizable(openglWindow);

	// We zijn klaar met 'window'
	window->end();
	// Laat 'window' zien
	window->show(argc, argv);
/*
window done
*/

	// Vraag een timeout aan voor over 1/25ste seconde
	Fl::add_timeout(1.0 / 25.0, timeoutFunc, NULL);
}

int GLWindow::handle(int event) {
	switch (event) {
	case FL_PUSH:
		if (Fl::event_button() == 1) {
			mouseButtonDown((float)Fl::event_x(), h() - (float)Fl::event_y());
			return 1;
		}
		return 0;
	case FL_RELEASE:
		if (Fl::event_button() == 1) {
			mouseButtonUp((float)Fl::event_x(), h() - (float)Fl::event_y());
			return 1;
		}
		return 0;
	case FL_DRAG:
		if (Fl::event_button() == 1) {
			mouseDrag((float)Fl::event_x(), h() - (float)Fl::event_y());
			return 1;
		}
		return 0;
	default:
		return Fl_Gl_Window::handle(event);
	}
}



void GLWindow::draw() {
	beginglDraw();

	doDrawing(opdrachtMenu->value());

	endglDraw();
}



void GLWindow::beginglDraw() {
	if (!valid()) {
		// setup viewport
		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

		GLsizei width, height;
		GLdouble aspect;

		width = w();
		height = h();
		if ((width == 0) || (height == 0)) return;

		if (width==0) aspect = (GLdouble)width;
		else aspect = (GLdouble)width/(GLdouble)height;

		glViewport(0, 0, width, height);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(45, aspect, 01, 50.0);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

		// set standard opengl stuff
		glEnable(GL_DEPTH_TEST);


	}

#ifndef MESA
	glDrawBuffer(GL_BACK);
#endif // !MESA
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

void GLWindow::endglDraw() {
#ifndef MESA
	glDrawBuffer(GL_BACK);
#endif // !MESA
}
